package cn.zniot.so.database;

import cn.zniot.so.application.constant.Constants;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

/**
 * 数据库连接池管理器
 */
public class ConnectionMgr {
	/**
	 * 单例
	 */
	private static ConnectionMgr instance = new ConnectionMgr();

	private ConnectionMgr() {
	}

	public static ConnectionMgr getInstance() {
		return instance;
	}

	/**
	 * 连接池
	 */
	private ComboPooledDataSource dbPool = null;

	/**
	 * 初始化数据库连接池
	 */
	public void initDB() throws Exception {
		try {
			this.dbPool = new ComboPooledDataSource();
			this.dbPool.setDriverClass(Constants.DB_DRIVER);
			this.dbPool.setJdbcUrl(Constants.DB_URL);
			this.dbPool.setUser(Constants.USER);
			this.dbPool.setPassword(Constants.PASSWD);

			// 初始化时获取三个连接，取值应在minPoolSize与maxPoolSize之间。Default: 3
			// initialPoolSize
			this.dbPool.setInitialPoolSize(Constants.DB_INITPOOL_SIZE);
			// 连接池中保留的最大连接数。Default: 15 maxPoolSize
			this.dbPool.setMaxPoolSize(Constants.DB_MAXPOOL_SIZE);
			// 连接池中保留的最小连接数。
			this.dbPool.setMinPoolSize(1);
			// 当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。Default: 3 acquireIncrement
			this.dbPool.setAcquireIncrement(1);
			// 最大空闲时间,25000秒内未使用则连接被丢弃。若为0则永不丢弃。Default: 0 maxIdleTime
			this.dbPool.setMaxIdleTime(1800);
			// 连接关闭时默认将所有未提交的操作回滚。Default: false autoCommitOnClose
			this.dbPool.setAutoCommitOnClose(false);
			// 当连接池用完时客户端调用getConnection()后等待获取新连接的时间，超时后将抛出SQLException,如设为0则无限期等待。单位毫秒。Default:
			// 0
			this.dbPool.setCheckoutTimeout(0);
			// c3p0是异步操作的，缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能通过多线程实现多个操作同时被执行。Default:
			// 3
			this.dbPool.setNumHelperThreads(3);
			// 定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个一显著提高测试速度。注意：
			// 测试的表必须在初始数据源的时候就存在。Default: null preferredTestQuery
			// this.dbPool.setPreferredTestQuery("select sysdate from dual");
			this.dbPool.setPreferredTestQuery("select now()");

			// 因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的
			// 时候都将校验其有效性。建议使用idleConnectionTestPeriod或automaticTestTable
			// 等方法来提升连接测试的性能。Default: false testConnectionOnCheckout
			this.dbPool.setTestConnectionOnCheckout(false);
			// 如果设为true那么在取得连接的同时将校验连接的有效性。Default: false
			// testConnectionOnCheckin
			this.dbPool.setTestConnectionOnCheckin(true); // 数据库重练
			// 每60秒检查所有连接池中的空闲连接。Default: 0 idleConnectionTestPeriod
//			this.dbPool.setIdleConnectionTestPeriod(60);
			this.dbPool.setIdleConnectionTestPeriod(6);
			// 定义在从数据库获取新连接失败后重复尝试的次数。Default: 30 acquireRetryAttempts
			this.dbPool.setAcquireRetryAttempts(30);
			// 两次连接中间隔时间，单位毫秒。Default: 1000 acquireRetryDelay
			this.dbPool.setAcquireRetryDelay(1000);
			// 获取连接失败将会引起所有等待连接池来获取连接的线程抛出异常。但是数据源仍有效
			// 保留，并在下次调用getConnection()的时候继续尝试获取连接。如果设为true，那么在尝试
			// 获取连接失败后该数据源将申明已断开并永久关闭。Default: false breakAfterAcquireFailure
			this.dbPool.setBreakAfterAcquireFailure(false); // 当测试连接不可用时会自动重新获得连接
															// 即数据库重连
		} catch (PropertyVetoException e) {
			throw new Exception("Init connectionPool failed：", e);
		}
	}

	/**
	 * 获取数据库连接
	 */
	public synchronized Connection getConnection() throws Exception {
		try {
			return this.dbPool.getConnection();
		} catch (SQLException e) {
			throw new Exception("Get dbConnection failed.", e);
		}
	}

	/**
	 * 销毁数据库连接池
	 */
	public void destroy() throws Exception {
		try {
			DataSources.destroy(this.dbPool);
		} catch (SQLException e) {
			throw new Exception("Destory connectionPool failed.", e);
		}
	}
}
